Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.

...powered by www.netzwerkartist.de...

 <<   zurück
Visual Basic 2005 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual Basic 2005

Visual Basic 2005
1.233 S., mit 2 CDs, 59,90 Euro
Galileo Computing
ISBN 3-89842-585-1
gp Kapitel 26 Datenbankzugriff mit ADO.NET
  gp 26.1 Eine kleine Einführung
  gp 26.2 Die Verbindung zu einer Datenquelle herstellen
    gp 26.2.1 Der Inhalt der Verbindungszeichenfolge
    gp 26.2.2 Die Authentifizierung
    gp 26.2.3 Das Öffnen einer Verbindung
    gp 26.2.4 Schließen einer Verbindung
    gp 26.2.5 Die Dauer des Verbindungsaufbaus
    gp 26.2.6 Eigenschaften eines »Connection«-Objekts
    gp 26.2.7 Die Ereignisse eines »Connection«-Objekts
    gp 26.2.8 Unterstützung bei Projekten mit grafischer Benutzeroberfläche
  gp 26.3 Das »Command«-Objekt
    gp 26.3.1 Erzeugen eines »Command«-Objekts
    gp 26.3.2 Ausführen des »Command«-Objekts
    gp 26.3.3 Aktionsabfragen mit »ExecuteNonQuery« absetzen
    gp 26.3.4 Auswahlabfragen mit »ExecuteReader«
    gp 26.3.5 Abfragen, die genau ein Ergebnis liefern
    gp 26.3.6 Parametrisierte Abfragen
    gp 26.3.7 Die Unterstützung des Visual Studios 2005
  gp 26.4 Der »DataAdapter« als Bindeglied zwischen Datenbank und verbindungslosen Objekten
    gp 26.4.1 Die Konstruktoren der Klasse »DataAdapter«
    gp 26.4.2 Den lokalen Datenspeicher mit der Methode »Fill« füllen
    gp 26.4.3 Abrufen von Schemainformationen
    gp 26.4.4 Die Unterstützung des Visual Studios 2005
  gp 26.5 Das »DataSet«-Objekt
    gp 26.5.1 Allgemeines
    gp 26.5.2 Ein »DataSet«-Objekt erzeugen
    gp 26.5.3 Das DataSet füllen
    gp 26.5.4 Tabellen- und Spaltenbezeichner zuordnen
  gp 26.6 »DataTable«-Objekte
    gp 26.6.1 Die Zeilen und Spalten in einer »DataTable«
    gp 26.6.2 Mit mehreren Tabellen arbeiten
    gp 26.6.3 Änderungen an einer »DataTable« vornehmen
    gp 26.6.4 Datenausgabe in WinForms mit dem Visual Studio 2005
  gp 26.7 Aktualisieren der Datenbank
    gp 26.7.1 Aktualisieren mit dem »CommandBuilder«-Objekt
    gp 26.7.2 Manuell gesteuerte Aktualisierungen
    gp 26.7.3 Aktualisieren mit »ExecuteNonQuery«
    gp 26.7.4 Manuelles Aktualisieren mit dem DataAdapter
    gp 26.7.5 Den zu aktualisierenden Datensatz in der Datenbank suchen
    gp 26.7.6 Den Benutzer über die fehlgeschlagenen Aktualisierungen informieren
    gp 26.7.7 Die konfliktverursachenden Datenzeilen bei der Datenbank abfragen


Galileo Computing

26.4 Der »DataAdapter« als Bindeglied zwischen Datenbank und verbindungslosen Objekten  downtop

Mit der Methode ExecuteNonQuery des Command-Objekts wird eine Aktionsabfrage ausgeführt, in der eine Datenzeile nach der anderen durchlaufen werden kann, während ExecuteReader ein DataReader-Objekt zurückliefert. Für ganz einfache Anforderungen mag das durchaus genügen, bei objektiver Betrachtung werden damit die Bedürfnisse der täglichen Praxis völlig unzureichend abgedeckt.

Was ist, wenn wir es dem Anwender ermöglichen wollen, beliebig zwischen den einzelnen Datensätzen zu navigieren? Wie kann ein Anwender die eingelesenen Datensätze aktualisieren? Wie kann seitens der Anwendung sichergestellt werden, dass bei der Aktualisierung Einschränkungen (Constraints) berücksichtigt werden?

Grundsätzlich lassen sich diese und viele weitere Fragen mit dem SqlCommand- und SqlDataReader-Objekt, allerdings unter erheblichem Programmieraufwand, beantworten. Aber denken wir einen Schritt weiter. Beide Objekte sind von einer geöffneten Verbindung zur Datenbank abhängig. Wollen wir es einem Anwender ermöglichen, durch die Datensätze zu navigieren, müssten wir die Verbindung zur Datenquelle einen längeren Zeitraum geöffnet halten oder die einzelnen Datensätze lokal zwischenspeichern.

Eine Verbindung länger als unbedingt notwendig geöffnet zu halten, ist aus vielerlei Hinsicht nicht akzeptabel. Stellen Sie sich nur eine Datenbank im Internet vor. Eine geöffnete Verbindung kostet Geld, und die Netzwerkressourcen werden belastet. Darüber hinaus ist die Anzahl der gleichzeitigen Zugriffe auf eine Datenbank begrenzt.

Eine optimale Lösung müsste mindestens die folgenden Fähigkeiten haben:

gp  Einlesen alle Datensätze und Zwischenspeichern derselben im lokalen Speicher.
gp  Automatisches Schließen der Verbindung nach dem Einlesen, um die Ressourcen zu schonen.
gp  Editierbarkeit der Daten.
gp  Zurückschreiben der Daten in die Datenbank.

Wir brauchen diesen Ansatz nicht selbst zu programmieren, er wird uns von ADO.NET angeboten. Als Bindeglied zwischen der Datenquelle und dem lokalen Speicher dient dazu ein speziell geschultes Objekt vom Typ SqlDataAdapter. Dieses hat die Fähigkeit, Daten aus einer Datenquelle abzufragen und in einer oder mehreren Tabellen des lokalen Speichers der Clientanwendung abzulegen. Da nicht nur eine, sondern x-beliebig viele Tabellen abgefragt werden können, müssen die Tabellen im lokalen Speicher von einem übergeordneten Objekt verwaltet werden. Hierbei handelt es sich um das DataSet, dem vielleicht wichtigsten Typ in der ADO.NET-Welt.

Darüber hinaus kann ein SqlDataAdapter-Objekt aber auch Änderungen, die ein Anwender in den Tabellen des lokalen Speichers vorgenommen hat, der Datenquelle zur Aktualisierung übermitteln.

Um die Netzwerk- und Datenbankserverbelastung so gering wie möglich zu halten, baut das SqlDataAdapter-Objekt nur dann eine Verbindung zur Datenbank auf, wenn dies notwendig ist. Das ist der Fall, wenn Daten abgefragt oder geänderte Daten zurückgeschickt werden. Damit wird die Netzbelastung so gering wie möglich gehalten und die Skalierbarkeit des Datenbankservers gewährleistet.

Im Zusammenhang mit einem SqlDataAdapter spielen auch SqlConnection- und SqlCommand-Objekte eine wichtige Rolle. Alle drei sind providerspezifisch und werden zu den verbundenen Typen des ADO.NET-Objektmodells gezählt. Die Daten im lokalen Speicher, die natürlich auch allesamt in speziellen Objekten verwaltet und organisiert werden, werden den unverbundenen Typen des ADO.NET-Objektmodells zugerechnet. Ein SqlDataAdapter kann daher als Bindeglied zwischen den verbundenen und den unverbundenen Objekten angesehen werden.


Galileo Computing

26.4.1 Die Konstruktoren der Klasse »DataAdapter«  downtop

SqlDataAdapter stellt die Verbindung zwischen einer Datenquelle und einem DataSet (ein Container, der mehrere Tabellen beherbergen kann) bzw. einer DataTable (das ist die Klasse, die eine Tabelle in einem DataSet beschreibt) her und füllt diese mit den angefragten Daten. SqlDataAdapter hat vier Konstruktoren, die wir uns ansehen sollten.


Public Sub New()
Public Sub New (ByVal command As SqlCommand)
Public Sub New (ByVal selectCommand As String, _
ByVal con As SqlConnection)
Public Sub New (ByVal selectCommand As String, _
ByVal connectionstring As String)

Ein SqlDataAdapter muss wissen, auf welcher Verbindung er einen Befehl absetzen soll. Zudem muss er selbstverständlich auch den abzusetzenden Befehl kennen. Die Konstruktoren bieten mehrere Kombinationsmöglichkeiten, dem DataAdapter die von ihm benötigten Informationen zu übergeben.

Die Eigenschaft »SelectCommand«

Verwenden Sie den parameterlosen Konstruktor, müssen Sie der Eigenschaft SelectCommand die Referenz auf ein SqlCommand-Objekt zuweisen.


Public Property SelectCommand As SqlCommand

Die Klasse SqlDataAdapter stellt weder eine Eigenschaft noch eine Methode bereit, mit der wir eine Verbindungszeichenfolge oder ein Connection-Objekt festlegen können. Das ist aber auch nicht weiter schlimm, da das Command-Objekt seinerseits selbst alle Verbindungsinformationen enthält.


Dim strCon As String = "..."
Dim con As SqlConnection = New SqlConnection(strCon)
Dim cmd As SqlCommand = _
New SqlCommand("SELECT * FROM authors", con)
Dim da As SqlDataAdapter = New SqlDataAdapter()
da.SelectCommand = cmd


Galileo Computing

26.4.2 Den lokalen Datenspeicher mit der Methode »Fill« füllen  downtop

Es lässt sich trefflich darüber streiten, welche Methode eines bestimmten Typs die wichtigste ist. Bei einem SqlDataAdapter-Objekt ist das auch nicht anders, aber meiner Meinung nach sind es zwei Methoden, die den elementaren Kern dieses Typs ausmachen:

gp  Fill
gp  Update

Mit der Methode Fill wird der lokale Datenspeicher mit dem Ergebnis einer SELECT-Abfrage gefüllt. Dazu wird für die Dauer der Abfrageoperation eine Verbindung zur Datenquelle geöffnet und nach deren Beendigung wieder geschlossen. Die empfangenen Daten kann der Anwender ändern, er kann auch Datensätze löschen oder neue hinzufügen. Während dieser Zeit besteht kein Kontakt zur Datenbank.

Zu einem späteren Zeitpunkt sollen die Änderungen natürlich auch in die Originaldatenquelle zurückgeschrieben werden. Dazu muss die Methode Update des SqlDataAdapters aufgerufen werden. Der SqlDataAdapter sorgt dafür, dass die Verbindung mit den bekannten Verbindungsinformationen erneut aufgebaut und nach Beendigung der Aktualisierung automatisch geschlossen wird. Die Aktualisierung der Datenquelle ist ein komplexes Thema, auf das später in diesem Buch noch eingegangen wird. Sie sollten aber bereits jetzt wissen, dass dem SqlDataAdapter auch bei der Aktualisierung eine wichtige Rolle zukommt.

Die Methode Fill wollen wir einschließlich aller Konsequenzen etwas genauer unter die Lupe nehmen. Fill ist vielfach überladen, wobei ich Ihnen zwei Überladungen vorstellen möchte:


Public Fill(DataTable) As Integer
Public Fill(DataSet) As Integer

Dem Aufruf wird entweder ein DataTable- oder ein DataSet-Objekt übergeben. Beide sind unabhängig vom .NET-Datenprovider und gehören zum Namespace System.Data. Ein DataTable-Objekt entspricht einer Tabelle in der Datenbank. Es hat die Spalten, die in der SELECT-Abfrage angegeben worden sind, und enthält die Datensätze, die das Ergebnis der SELECT-Abfrage bilden. Ein DataSet-Objekt können Sie sich als einen Container für mehrere DataTable-Objekte vorstellen.

Beispielprogramm

Im folgenden Beispiel wird die Tabelle in einem DataSet gefüllt mit drei Spalten der Tabelle authors. Dabei soll es jedoch nicht bleiben. Nach dem Füllen wollen wir uns auch noch vom Erfolg unserer Bemühungen überzeugen und die Datenzeilen im Konsolenfenster sehen. Das ist die Aufgabe der For Each-Schleife, auf die ich an dieser Stelle noch nicht eingehen werde.


' ------------------------------------------------------
' Beispiel: ...\Kapitel 26\DataSetFüllen
' ------------------------------------------------------
Imports System.Data.SqlClient
Module Module1
Sub Main()
Dim con As SqlConnection = New SqlConnection()
con.ConnectionString = "..."
Dim cmd As New SqlCommand
cmd.Connection = con
cmd.CommandText = "SELECT au_id, au_lname, city FROM authors"
Dim ds As New DataSet
Dim da As SqlDataAdapter = New SqlDataAdapter(cmd)
da.Fill(ds)
For Each row As DataRow In ds.Tables(0).Rows
Console.WriteLine("{0,-13}{1,-25}{2}", _
row("au_id"), row("au_lname"), row("city"))
Next
Console.ReadLine()
End Sub
End Module

Nach dem Füllen einer DataTable oder eines DataSets gibt es keine Verbindung mehr zum DataAdapter. Das bedeutet, dass weder der DataAdapter eine Referenz auf das Objekt hat, das er gefüllt hat, noch weiß das gefüllte Objekt, von wem es gefüllt worden ist.

Verbindungen öffnen und schließen

Mit


Dim da As SqlDataAdapter = New SqlDataAdapter(cmd)

wird das SqlDataAdapter-Ojekt erzeugt und dabei über das SqlCommand-Objekt cmd unter anderem die Referenz auf das Verbindungsobjekt con übergeben. Auffällig ist, dass niemals die Open-Methode aufgerufen wird, um die Abfrage zu übermitteln. Das ist aber auch nicht nötig, denn mit


da.Fill(tbl)

wird der DataAdapter die Verbindung selbstständig öffnen, die Ergebnisse abfragen und die Verbindung auch selbstständig schließen. Das steht ganz im Gegensatz zu den Execute-Methoden des Command-Objekts, die dieses intelligente Verhalten nicht zeigen und auf das ausdrückliche Öffnen der Verbindung angewiesen sind.

Sie dürfen allerdings auch explizit eine Verbindung vor dem Aufruf von Fill mit Open öffnen. Der DataAdapter wird das bemerken und die Verbindung nicht in Eigenregie schließen, wenn die Abfrageresultate eingetroffen sind. Es liegt dann in Ihrer Verantwortung, dafür zum frühestmöglichen Zeitpunkt zu sorgen.


...
con.Open()
da.Fill(tbl)
con.Close()

Besonderheiten der »Fill«-Methode

Angenommen, es wird zweimal hintereinander die Fill-Methode aufgerufen, ohne vor dem zweiten Aufruf das DataSet oder die DataTable zu leeren, also:


...
da.Fill(ds)
...
da.Fill(ds)

Die Idee, die dem doppelten Aufruf zugrunde liegt, könnte die Aktualisierung des DataSets sein. Allerdings werden nun die Datensätze in der Tabelle doppelt erscheinen. Mit dem ersten Aufruf der Fill-Methode wird das DataTable-Objekt erzeugt, und die Datensätze werden hineingeschrieben, mit dem zweiten werden die Datensätze einfach noch einmal aus der Datenquelle bezogen und in die schon vorhandene Tabelle kopiert.

Der Grund dieses im ersten Moment etwas sonderbaren Verhaltens ist darin zu finden, dass die Primärschlüsselspalte der Originaltabelle nicht automatisch zur Primärschlüsselspalte der DataTable wird. Primärschlüssel dienen unter anderem zur Vermeidung von duplizierten Datensätzen und müssen in der Datenquelle festgelegt werden. Die DataTable übernimmt diese jedoch nicht.


Hinweis

Das DataTable-Objekt hat eine Eigenschaft PrimaryKey. Wird diese gesetzt, wird der Data-Adapter die doppelten Zeilen finden und die alten Werte verwerfen. Mehr Informationen darüber erhalten erhalten Sie später.


Wird die Methode Fill hintereinander auf verschiedene DataAdapter aufgerufen, wird jeweils eine neue Verbindung benötigt. Daran ändert sich auch nichts, wenn allen Aufrufen dieselbe Verbindungszeichenfolge zugrunde liegt.


...
Dim daAuthors As SqlDataAdapter = _
New SqlDataAdapter(strSQL1, con)
Dim daTitles As SqlDataAdapter = _
New SqlDataAdapter(strSQL2, con)
Dim dsAuthors As New DataSet
Dim dsTitles As New DataSet
daAuthors.Fill(dsAuthors)
...
daTitles.Fill(dsTitles)

Der zweimalige Aufruf von Fill hat zur Folge, dass in diesem Codefragment unnötigerweise Leistungseinbußen in Kauf genommen werden müssen, weil in jedem Fall ein impliziter Open- bzw. Close-Aufruf auf die Verbindung erfolgt.

Wollen Sie sicherstellen, dass eine Verbindung von beiden DataAdapter-Objekten gleichermaßen benutzt wird, müssen Sie die Steuerung selbst übernehmen und mit der Open-Methode die Verbindung vor dem ersten Füllen des DataDets bzw. der DataTable öffnen.


...
con.Open()
daAuthors.Fill(dsAuthors)
...
daTitles.Fill(dsTitles)
con.Close()

Das DataSet verwaltet alle Tabellen, die in ihm enthalten sind, in einer Collection, deren Referenz die Eigenschaft Tables des DataSets liefert. Über


dsTables(0)

können Sie daher auf die erste Tabelle in der Tabellenauflistung zugreifen, beispielsweise um die Daten auszuwerten. Angegeben wird entweder der Index der Tabelle in der Auflistung oder deren Name. Standardmäßig ist der Name der ersten Tabelle Table, der der zweiten Table1 usw. Mit einer Überladung der Fill-Methode können Sie schon beim Füllen des DataSets einen besser beschreibenden Bezeichner festlegen, z.  B.:


da.Fill(ds, "Autoren")

Sich für diese Überladung zu entscheiden, kann vorteilhaft sein, wenn Sie das DataSet mit mehreren Tabellen füllen wollen.


cmd.CommandText = "SELECT au_id, au_lname, city FROM authors"
Dim da As SqlDataAdapter = New SqlDataAdapter(cmd)
Dim ds As New DataSet
con.Open()
da.Fill(ds, "Autoren")
cmd.CommandText = "SELECT * FROM titles"
da.Fill(ds, "Titel")
con.Close()

Die Referenz auf die Tabelle, die alle Titel enthält, können Sie jetzt gleichwertig mit


ds.Tables(1)

oder


ds.Tables("Titel")

abrufen.


Galileo Computing

26.4.3 Abrufen von Schemainformationen  downtop

Liegt eine DataTable im Speicher, können die Daten nicht nur angezeigt, sondern auch verändert werden. Die Aktualisierungen werden mit der Update-Methode des DataAdapters an die Originaldatenbank geschickt und dort gespeichert. Viele Spalten einer Tabelle unterliegen aber Gültigkeitsregeln: Beispielsweise lassen einige nur eine maximale Zeichenanzahl zu, andere schreiben einen eindeutigen Eintrag innerhalb der Datensätze der Tabelle vor oder lassen keinen NULL-Wert zu.

Eine DataTable oder ein DataSet, das wir mit Fill füllen, ist sehr dumm. Es enthält zwar alle angeforderten Daten, weiß aber nichts von den Gültigkeitsregeln, die in der Datenbank festgelegt worden sind. Die Folge ist, dass in der Anwendung die Daten beliebig verändert werden können, ohne dass eine Überprüfung erfolgt. Der Versuch, die Änderungen in die Datenbank zu schreiben, wird jedoch scheitern, weil die Datenbank vor der endgültigen Aktualisierung zuerst die Änderungen mit den Gültigkeitsregeln vergleicht und die Verletzung feststellen wird. Es kommt zu einer Ausnahme in der Anwendung.

Im folgenden Beispiel wird der Datensatz eines bestimmten Autors anhand der Autoren-ID aus der authors-Tabelle abgefragt und – falls die Abfrage einen Datensatz liefert – angezeigt. Der Eintrag in der Spalte city, der Wohnort des Autors, kann nach Aufforderung neu festgelegt werden. Allerdings ist diese Spalte auf maximal 20 Zeichen in der Originaltabelle beschränkt.

Die Aktualisierungslogik mit Update soll hier noch nicht erklärt werden, ebenso wenig die Ausgabe der Abfrage an der Konsole.


' ------------------------------------------------------
' Beispiel: ...\Kapitel 26\DataSetOhneSchema
' ------------------------------------------------------
Imports System.Data.SqlClient
Module Module1
Sub Main()
Dim con As New SqlConnection
con.ConnectionString = "..."
Dim cmd As New SqlCommand
cmd.Connection = con
cmd.CommandText = "SELECT * FROM authors " & _
"WHERE au_id='172–32–1176'"
Dim da As SqlDataAdapter = New SqlDataAdapter(cmd)
Dim ds As New DataSet
da.Fill(ds)
Dim tbl As DataTable = ds.Tables(0)
' Anzeige der Daten
If tbl.Rows.Count > 0 Then
Dim row As DataRow = tbl.Rows(0)
' Anzeige aller Spalten des Abfrageergebnisses
For Each column As DataColumn In tbl.Columns
Console.WriteLine(row(column))
Next
End If
Console.WriteLine()
Console.Write("Neuer Wohnort: ")
' tbl.Columns("city").MaxLength = 20
tbl.Rows(0)("city") = Console.ReadLine()
' Datenbank aktualisieren
Dim cb As SqlCommandBuilder = New SqlCommandBuilder(da)
da.Update(tbl)
End Sub
End Module

Beachten Sie, dass die Ausnahme erst nach dem Aufruf der Update-Methode auftreten wird, ein Zeichen dafür, dass die Datenbank die Aktualisierung abgelehnt hat. Der Ansatz, der Datenbank die Verantwortung zu überlassen, ist keine gute Lösung. Sicher werden wir deren Überprüfung nicht abstellen können, aber wir können den Netzverkehr entlasten, der unweigerlich auftritt, wenn die Datenbank eine Aktualisierung ablehnt. Das wird sich besonders bei stark frequentierten Datenbanken positiv auswirken.

Besser ist es, bereits die Eingabe des Anwenders zu überprüfen und ihm gegebenenfalls mitzuteilen, wenn er gegen die in der Datenbank festgelegten Gültigkeitsregeln verstößt. Dazu werden jedoch die Schemainformationen benötigt. Im Beispiel sind diese noch auskommentiert.


tbl.Columns("city").MaxLength = 20

Sie können diese Auskommentierung aufheben und werden feststellen, dass jetzt die Ausnahme nicht mehr in der Zeile mit der Update-Methode auftritt, sondern in der Eingabeanweisung:

tbl.Rows(0)("city") = Console.ReadLine()

Die Datenbank wird, da die Spalte city in der Tabelle des DataSets mit der Eigenschaft MaxLength auf eine Maximallänge von 20 Zeichen eingeschränkt wird, in jedem Fall gültige Daten erhalten.

Es gibt drei Möglichkeiten, einem Dataset bzw. einer DataTable Schemainformationen bereitzustellen:

gp  Die Schemainformationen werden von der Datenbank mit der Methode FillSchema des DataAdapters bezogen.
gp  Die Gültigkeitsregeln werden mittels Programmcode für alle betreffenden Tabellen und Spalten festgelegt.
gp  Die Methode ReadXmlSchema des DataSet-Objekts, um Schemainformationen in eine XSD-Datei (XML Schema Definition) zu speichern.
gp  Um die in der Anwendung eingegebenen Daten mittels Programmcode zu überprüfen, hat das DataColumn-Objekt, mit dem eine Spalte der Tabelle beschrieben wird, einige Eigenschaften:
    gp  ReadOnly
    gp  AllowDBNull
    gp  MaxLength
    gp  AutoIncrement
    gp  Unique.

Auch ein DataTable-Objekt unterstützt das anwendungsseitige Definieren von Einschränkungen. Unter anderem kann der Eigenschaft PrimaryKey die Primärschlüsselspalte bzw. können die Primärschlüsselspalten einer Tabelle übergeben werden. Wir werden uns, wenn wir DataTable und DataColumn erörtern, mit den genannten Eigenschaften noch eingehend beschäftigen.

Die Methode »FillSchema«

Die einfachste Variante, Schemainformationen bereitzustellen, ist die Methode FillSchema, mit der Sie Schemainformationen für ein DataSet oder eine DataTable direkt bei der Datenbank abrufen können. Basis ist dabei das in SelectCommand beschriebene SELECT-Kommando.

FillSchema ist mehrfach überladen. Zwei Überladungen seien an dieser Stelle gezeigt.


Public Function FillSchema(DataTable, SchemaType) As DataTable
Public Overrides Function FillSchema(DataSet, SchemaType) As DataTable()

Allen Überladungen ist gemein, dass sie ein Argument vom Typ der Enumeration SchemaType erwarten. Diese Aufzählung hat zwei Mitglieder: Source und Mapped. Über diesen Parameter wird gesteuert, ob der DataAdapter die Zuordnungen, die in der DataTableMappingCollection und der DataColumnMappingCollection angegeben sind, verwenden soll.


Tabelle 26.3     Die Mitglieder der Enumeration »SchemaType«

Member Beschreibung
Mapped Der DataAdapter verwendet die Zuordnungen der Spalten in der TableMappings-Auflistung.
Source Der DataAdapter ignoriert die Zuordnungen der Spalten in der TableMappings-Auflistung.

Der Aufruf der Methode mit beispielsweise


da.FillSchema(ds, SchemaType.Source)

ist einerseits natürlich sehr bequem, aber andererseits dürfen Sie nicht vergessen, dass dabei sowohl das Netzwerk als auch die Datenbank belastet werden. Die in Tabelle 26.3 erwähnten TableMappings werden weiter unten noch angesprochen.

Auf der Buch-CD finden Sie das Beispiel FillSchemaDemo, das auf dem Code des Beispiels DataSetOhneSchema basiert, aber zusätzlich das Schema der Tabelle von der Originaldatenbank bezieht.

Die Methoden »WriteXmlSchema« und »ReadXmlSchema«

Mit den Methoden WriteXmlSchema und ReadXmlSchema des Datasets können Sie die Schemainformationen in eine XML-Schemadatei schreiben und später auswerten. XML-Schemadateien haben üblicherweise die Dateiendung .XSD.

Bevor Sie das Schema eines DataSets in einer Schemadatei speichern, muss das Schema bekannt sein. Sie können sich daher zur Entwicklungszeit das Schema einmalig mit FillSchema besorgen und dann mit WriteXmlSchema in einer Datei speichern.


ds.WriteXmlSchema("C:\MyDataSetSchema.xsd")

Damit die XSD-Datei auch Nutzen bringt, muss sie zusammen mit der Applikation ausgeliefert werden.

Zur Laufzeit erzeugen Sie zuerst das DataSet-Objekt, lesen anschließend die Schemadatei ein und füllen das DataSet mit den Daten.


...
Dim ds As New DataSet
ds.ReadXmlSchema("C:\MyDataSetSchema.xsd")
da.Fill(ds)

In Abbildung 26.3 sehen Sie die Schemadatei, die auf einer Abfrage basiert, die alle Spalten eines bestimmten Autors aus der Tabelle authors wiedergibt.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 26.3     Schemadatei (.XSD-Datei)

Das Schema mit Programmcode erzeugen

Verhältnismäßig aufwändig ist die programmatische Bereitstellung eines Schemas. Mit den Eigenschaften AllowDBNull, MaxLength, Unique einer DataColumn sowie PrimaryKey einer DataTable können Sie Datenüberprüfungsmechanismen implementieren. Mit ReadOnly=True haben Sie zudem die Möglichkeit, gültige Daten vor einer Veränderung durch den Benutzer zu schützen.

Typischer Programmcode könnte beispielsweise wie folgt aussehen:


ds.Tables(0).Columns("au_id").MaxLength = 11
ds.Tables(0).Columns("au_lname").MaxLength = 40
ds.Tables(0).Columns("au_fname").MaxLength = 20
ds.Tables(0).Columns("contract").AllowDBNull = False
ds.Tables(0).PrimaryKey = _
New DataColumn() {ds.Tables(0).Columns("au_id")}

Sind in einem DataSet mehrere Tabellen enthalten, die miteinander in Beziehung stehen, muss gegebenenfalls auch die Beziehung codiert werden. Die Klasse DataRelation, welche die Beziehung zwischen zwei Tabellen beschreibt, werden wir uns später noch ansehen.

Die ConstraintsCollection einer DataTable

Ein DataTable-Objekt veröffentlicht über die Eigenschaft Constraints die Referenz auf eine Auflistung vom Typ ConstraintCollection.


Public ReadOnly Property Constraints As ConstraintCollection

In der Auflistung werden Constraint-Objekte verwaltet, mit denen Einschränkungen der DataTable definiert werden.

Constraint ist eine abstrakte Basisklasse, von der es zwei Ableitungen gibt:

gp  UniqueConstraint
gp  ForeignKeyConstraint

Eine Einschränkung beschreibt eine Regel, mit der die Datenintegrität gewährleistet wird. Löschen Sie beispielsweise einen Datensatz in einer Tabelle, die mit anderen Tabellen verknüpft ist, muss sichergestellt werden, dass in den verknüpften Tabellen der entsprechende Datensatz ebenfalls gelöscht wird, der Wert der betroffenen Spalte auf NULL gesetzt oder ein Standardwert festgelegt wird. Zudem kann eine Einschränkung auch so wirken, dass die Aktion überhaupt nicht ausgeführt wird. Die beschriebenen Einschränkungen in diesem Fall werden durch ein ForeignKeyConstraint-Objekt gesteuert.

ForeignKeyConstraint-Objekte explizit zu erzeugen ist in den meisten Fällen nicht nötig. Beim Erzeugen einer DataRelation zwischen zwei DataTable-Objekten innerhalb eines DataSets wird ein ForeignKeyConstraint-Objekt automatisch erstellt.

Mit der Eigenschaft Unique einer DataColumn können Sie angeben, ob die Werte in einer Spalte eindeutig sind. Dazu legen Sie

myDataColumn.Unique=True

fest. Gleichzeitig wird der ConstraintCollection der DataTable ein Objekt vom Typ der Klasse UniqueConstraint hinzugefügt. Normalerweise brauchen Sie also dieses Objekt nicht per Programmcode zu erzeugen. Nur wenn Sie sicherstellen müssen, dass die Kombination aus mehreren Spalten eindeutig ist, wird Ihnen keine andere Möglichkeit bleiben.

Auch die Festlegung einer Primärschlüsselspalte mit der Eigenschaft PrimaryKey der DataTable oder die Kombination mehrerer Spalten zu einem Primärschlüssel wird die implizite Erstellung eines UniqueConstraint-Objekts zur Folge haben.


Galileo Computing

26.4.4 Die Unterstützung des Visual Studios 2005  toptop

Jetzt wollen wir uns die Unterstützung des Visual Studios 2005 ansehen, um einen SqlData-Adapter zu erstellen, der ebenfalls als Steuerelement nach vorhergehendem Hinzufügen in der Lasche Daten der Toolbox angeboten wird. Ziehen Sie das Control in den Forms Designer, öffnet sich ein Dialog, der Sie durch die Konfiguration des DataAdapters führt.

Im ersten Schritt muss die Verbindung angegeben werden, auf welcher der SqlDataAdapter seine Aufgaben ausführen soll. Haben Sie bereits ein SqlConnection-Control in das Projekt gezogen, können Sie auch diese Verbindung auswählen. Sie können aber auch eine neue Verbindung definieren, indem Sie auf die entsprechend beschriftete Schaltfläche klicken.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 26.4     Konfiguration der Verbindung des SqlDataAdapters

Im zweiten Schritt geben Sie den Abfragetyp an. Dazu können Sie eine SQL-Anweisung festlegen, eine gespeicherte Prozedur erstellen oder auf eine existierende gespeicherte Prozedur zurückgreifen (Abbildung 26.5).

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 26.5     Auswahl des Befehlstyps

Fällt Ihre Entscheidung auf SQL-Anweisungen verwenden, öffnet sich ein Fenster, in dem Sie entweder direkt das SQL-Kommando eingeben oder den Abfrage-Generator starten können, der bereits in Abbildung 26.2 gezeigt wurde.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 26.6     Dialogfenster zum Erzeugen von SQL-Anweisungen

Die Schaltfläche Erweiterte Optionen... öffnet ein Dialogfenster, dessen Auswahloptionen im Zusammenhang mit der Aktualisierung der Datenbank stehen (siehe Abbildung 26.7). Am Ende dieses Kapitels werden wir das Aktualisieren von Daten erörtern. Daher gehe ich an dieser Stelle nicht näher auf die Einstellmöglichkeiten und deren Konsequenzen ein.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 26.7     Optionseinstellungen, die im Zusammenhang mit der Aktualisierung stehen

Aus allen Angaben, die Sie gemacht haben, generiert der Assistent die Verbindungsinformationen und erstellt die Auswahl- und Aktualisierungsanweisungen für das betreffende Kommando.

 <<   zurück
  
  Zum Katalog
Zum Katalog: Visual Basic 2005
Visual Basic 2005
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Visual C# 2005






 Visual C# 2005


Zum Katalog: Fortgeschrittene Programmierung mit Visual C# 2005






 Fortgeschrittene
 Programmierung
 mit Visual C# 2005


Zum Katalog: Das Programmierhandbuch SQL Server 2005






 Das Programmier-
 handbuch
 SQL Server 2005


Zum Katalog: Einstieg in Visual Basic 2005






 Einstieg in
 Visual Basic 2005


Zum Katalog: Einstieg in Visual C# 2005






 Einstieg in
 Visual C# 2005


Zum Katalog: Konzepte und Lösungen für Microsoft-Netzwerke






 Konzepte und
 Lösungen für
 Microsoft-Netzwerke


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo








Copyright © Galileo Press 2007
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de